import tkinter as tk
from tkinter import messagebox
import random
import string
import requests
from datetime import datetime, timedelta  # 添加timedelta导入
from tkinter import ttk, scrolledtext
from tkinter.scrolledtext import ScrolledText
import os
import time
import json
import subprocess
import sys

# 全局变量声明添加retry_dialog
global step_count, distance, calories, start_time, retry_dialog
step_count = 0
distance = 0.0
calories = 0.0
start_time = None
retry_dialog = None

class RetryDialog:
    def __init__(self, master, max_retries=10):
        self.master = master
        self.retry_count = 0
        self.max_retries = max_retries
        self.dialog = tk.Toplevel(master)
        self.dialog.title("自动重试中...")
        self.info_label = tk.Label(self.dialog, text=f"正在尝试第 {self.retry_count} 次重试")
        self.info_label.pack(pady=10)
        self.retry_button = tk.Button(self.dialog, text="手动重传", command=self.manual_retry, state=tk.DISABLED)
        self.retry_button.pack(pady=10)

    def update_retry_info(self, retry_count):
        if retry_count < self.max_retries:
            self.info_label.config(text=f"正在尝试第 {retry_count + 1} 次重试")
        else:
            self.info_label.config(text="自动重传失败，请手动重传")
            self.retry_button.config(state=tk.NORMAL)

    def manual_retry(self):
        self.retry_button.config(state=tk.DISABLED)
        self.info_label.config(text="正在手动重试...")
        # 触发手动重传逻辑
        global retry_upload_flag
        retry_upload_flag = True
        self.dialog.destroy()


#使用凯撒密码加密文本
def caesar_cipher_encrypt(text, shift=3):
    """使用凯撒密码加密文本"""
    encrypted_text = ""
    for char in text:
        if char.isalpha():  # 只加密字母
            shift_base = 65 if char.isupper() else 97
            encrypted_text += chr((ord(char) - shift_base + shift) % 26 + shift_base)
        else:
            encrypted_text += char  # 非字母字符直接添加
    return encrypted_text

#将上传失败的数据保存到本地文件
def save_failed_upload(machine_id, data, check_sum, time_str):
    """将上传失败的数据保存到本地文件"""
    os.makedirs('./failed_uploads', exist_ok=True)  # 确保目录存在
    filename = f"./failed_uploads/{machine_id}_{time_str}.txt"
    with open(filename, 'w') as file:
        file.write(f"machine_id: {machine_id}\n")
        file.write(f"time: {time_str}\n")
        for key, value in data.items():
            file.write(f"{key}: {value}\n")
        file.write(f"check_sum: {check_sum}\n")


#成格式为三个大写字母加5个数字的机器码
def generate_machine_id():
    """生成格式为三个大写字母加5个数字的机器码"""
    letters = ''.join(random.choices(string.ascii_uppercase, k=3))
    numbers = ''.join(random.choices(string.digits, k=5))
    return f"{letters}{numbers}"

#计算输入字符串的校验和，这里采用ASCII值之和并对100取余
def calculate_checksum(input_string):
    """计算输入字符串的校验和，这里采用ASCII值之和并对100取余"""
    return sum(ord(str(c)) for c in input_string) % 100

#尝试在服务器上注册机器码
def register_machine(machine_id):
    """尝试在服务器上注册机器码"""
    url = "http://47.108.221.119:5000/register"  # 替换为你的服务器地址
    current_time = datetime.now().strftime('%Y%m%d%H%M%S')
    concatenated_str = machine_id + current_time
    check_sum = calculate_checksum(concatenated_str)
    
    payload = {
        "data": {
            "machine_id": machine_id,
            "time": current_time,
            "check_sum": check_sum
        }
    }
    
    try:
        response = requests.post(url, json=payload, timeout=10)  # 增加timeout参数
        if response.status_code == 201:
            return True, None
        else:
            return False, response.json().get("message", "Unknown error")
    except requests.exceptions.RequestException as e:
        report_error(machine_id, f"Registration error: {str(e)}")
        return False, str(e)

#上传错误信息到服务器
def report_error(machine_id, error_message):
    """上传错误信息到服务器"""
    current_time = datetime.now().strftime('%Y%m%d%H%M%S')
    url = "http://47.108.221.119:5000/errorReport"
    payload = {
        "data": {
            "time": current_time,
            "error": error_message
        },
        "machine_id": machine_id
    }
    try:
        response = requests.post(url, json=payload)
        if response.status_code != 201:
            print(f"Failed to report error: {response.text}")
    except requests.exceptions.RequestException as e:
        print(f"Error reporting error: {str(e)}")

# 修改上传函数
def upload_data(machine_id, data, check_sum, time_str, message_box, frame):
    """改进后的上传函数"""
    url = "http://47.108.221.119:5000/upload"
    
    try:
        response = requests.post(url, json={
            "machine_id": machine_id,
            "data": data
        }, timeout=15)
        
        if response.status_code == 201:
            # 清除重传状态并重置计数器
            frame.retry_info['count'] = 0  # 新增重置计数器
            frame.retry_info['label'].config(text="")
            frame.retry_info['button'].config(state=tk.DISABLED)
            if frame.retry_info['job_id']:
                root.after_cancel(frame.retry_info['job_id'])
            message_box.insert(tk.END, f"上传成功: {time_str}\n")
        else:
            raise Exception(f"服务器返回错误: {response.status_code}")
            
    except Exception as e:
        message_box.insert(tk.END, f"上传失败: {str(e)}\n")
        start_auto_retry(frame, machine_id)

        
def start_auto_retry(frame, machine_id):
    """自动重传逻辑"""
    if frame.retry_info['count'] < 10:
        frame.retry_info['count'] += 1
        status_text = f"自动重传中 ({frame.retry_info['count']}/10)"
        frame.retry_info['label'].config(text=status_text, fg="blue")
        
        # 安排下次重试
        frame.retry_info['job_id'] = root.after(
            10000, 
            lambda: retry_upload(frame, machine_id)
        )
    else:
        # 超过次数启用手动重传
        frame.retry_info['label'].config(text="自动重传失败", fg="red")
        frame.retry_info['button'].config(state=tk.NORMAL)

def retry_upload(frame, machine_id):
    """执行实际重传操作"""
    data = frame.retry_info['last_data']
    check_sum = frame.retry_info['last_checksum']
    time_str = frame.retry_info['last_time']
    
    if data and check_sum and time_str:
        upload_data(machine_id, data, check_sum, time_str, frame.message_box, frame)

def start_manual_retry(frame, machine_id):
    """手动重传触发逻辑"""
    # 重置计数器
    frame.retry_info['count'] = 0
    frame.retry_info['label'].config(text="正在手动重传...", fg="green")
    frame.retry_info['button'].config(state=tk.DISABLED)
    
    # 立即触发重传
    retry_upload(frame, machine_id)
    
    # 安排状态重置
    root.after(15000, lambda: (
        frame.retry_info['label'].config(text=""),
        frame.retry_info['button'].config(state=tk.NORMAL)
    ) if frame.retry_info['count'] >= 10 else None)


# 定义打开查询窗口的函数
def open_query_window(machine_id):
    try:
        # 获取当前 Python 解释器的绝对路径
        python_path = sys.executable
        # 假设 chaxun.py 文件在 C:\Users\Administrator\Desktop\jianshen 目录下
        script_path = r'C:\Users\Administrator\Desktop\jianshen\chaxun.py'
        # 调用 chaxun.py 并传递机器码作为参数
        subprocess.Popen([python_path, script_path, machine_id])
    except Exception as e:
        messagebox.showerror("错误", f"打开查询窗口时出错: {e}")
        report_error(machine_id, f"Open query window error: {str(e)}")

# 定义打开设置目标窗口的函数
def open_set_goal_window(machine_id):
    try:
        # 获取当前 Python 解释器的绝对路径
        python_path = sys.executable
        # 假设 set-goal.py 文件在 C:\Users\Administrator\Desktop\jianshen 目录下
        script_path = r'C:\Users\Administrator\Desktop\jianshen\set-goal.py'
        # 调用 set-goal.py 并传递机器码作为参数
        subprocess.Popen([python_path, script_path, machine_id])
    except Exception as e:
        messagebox.showerror("错误", f"打开设置目标窗口时出错: {e}")

# 定义打开获取目标剩余窗口的函数
def open_remain_window(machine_id):
    """定义打开获取目标剩余窗口的函数"""
    try:
        # 获取当前 Python 解释器的绝对路径
        python_path = sys.executable
        # 假设 remain.py 文件在 C:\Users\Administrator\Desktop\jianshen 目录下
        script_path = r'C:\Users\Administrator\Desktop\jianshen\remain.py'
        # 调用 remain.py 并传递机器码作为参数
        subprocess.Popen([python_path, script_path, machine_id])
    except Exception as e:
        messagebox.showerror("错误", f"打开获取目标剩余窗口时出错: {e}")

# 定义打开advice窗口的函数
def open_advice_window(machine_id):
    try:
        # 获取当前 Python 解释器的绝对路径
        python_path = sys.executable
        # 假设 advice.py 文件也在 C:\Users\Administrator\Desktop\jianshen 目录下
        script_path = r'C:\Users\Administrator\Desktop\jianshen\advice.py'
        # 调用 advice.py 并传递机器码作为参数
        subprocess.Popen([python_path, script_path, machine_id])
    except Exception as e:
        messagebox.showerror("错误", f"打开建议窗口时出错: {e}")
        report_error(machine_id, f"Open advice window error: {str(e)}")


# 定义打开 pipei 窗口的函数
def open_match_window(machine_id):
    try:
        # 获取当前 Python 解释器的绝对路径
        python_path = sys.executable
        # 假设 pmatchipei.py 文件在 C:\Users\Administrator\Desktop\jianshen 目录下
        script_path = r'C:\Users\Administrator\Desktop\jianshen\match.py'
        # 调用 match.py 并传递机器码作为参数
        subprocess.Popen([python_path, script_path, machine_id])
    except Exception as e:
        messagebox.showerror("错误", f"打开 match 窗口时出错: {e}")

        
#为每个机器码创建一个新的Frame，并在其中显示机器码和实时数据
def create_frame_for_machine(container, machine_id):
    """为每个机器码创建一个新的Frame，并在其中显示机器码和实时数据"""
    frame = tk.Frame(container, borderwidth=1, relief="solid")
    label = tk.Label(frame, text=machine_id, font=("Helvetica", 16))
    label.pack(pady=10, padx=10)

    labels = ['心率', '呼吸率', '体温', '血压', '步数', '运动距离', '消耗的卡路里', '运动时长']
    entries = {}

    message_box = scrolledtext.ScrolledText(frame, height=5, width=40)
    message_box.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)

    # 添加查询按钮
    query_button = tk.Button(frame, text="查询", command=lambda: open_query_window(machine_id))
    query_button.pack(pady=10)

    # 添加设置目标按钮
    set_goal_button = tk.Button(frame, text="设置目标", command=lambda: open_set_goal_window(machine_id))
    set_goal_button.pack(pady=10)

    # 添加获取目标剩余按钮
    get_remain_button = tk.Button(frame, text="获取目标剩余", command=lambda: open_remain_window(machine_id))
    get_remain_button.pack(pady=10)
    
    # 添加获取建议按钮
    get_advice_button = tk.Button(frame, text="获取建议", command=lambda: open_advice_window(machine_id))
    get_advice_button.pack(pady=10)

    # 添加获取匹配按钮
    get_match_button = tk.Button(frame, text="匹配小程序", command=lambda: open_match_window(machine_id))
    get_match_button.pack(pady=10)

    # 新增状态显示区域
    status_frame = tk.Frame(frame)
    status_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=5)

    # 重传状态标签
    retry_label = tk.Label(status_frame, text="", fg="blue")
    retry_label.pack(side=tk.LEFT)

    # 手动重传按钮（初始隐藏）
    manual_retry_btn = tk.Button(
        status_frame, 
        text="手动重传", 
        command=lambda: start_manual_retry(frame, machine_id),
        state=tk.DISABLED
    )
    manual_retry_btn.pack(side=tk.RIGHT)

    # 存储设备相关状态
    frame.retry_info = {
        'count': 0,
        'label': retry_label,
        'button': manual_retry_btn,
        'job_id': None,
        'last_data': None,
        'last_checksum': None,
        'last_time': None
    }

    # 原有的 update_data 函数及后续代码保持不变
    def update_data():
        global step_count, distance, calories, start_time
        if start_time is None:
            start_time = datetime.now()

        elapsed_time = datetime.now() - start_time

        prev_step_count = step_count
        prev_distance = distance
        prev_calories = calories

        new_steps = random.randint(1, 3)
        step_count += new_steps
        distance += new_steps * 0.8
        calories += new_steps * 0.05

        heart_rate = random.randint(80, 120)
        breath_rate = random.randint(15, 25)
        body_temp = random.uniform(36.5, 37.5)
        systolic_pressure = random.randint(110, 130)
        diastolic_pressure = random.randint(70, 80)
        blood_pressure = f"{systolic_pressure}/{diastolic_pressure}"

        current_time = datetime.now().strftime('%Y%m%d%H%M%S')

        concatenated_str = (
            current_time +
            str(heart_rate) +
            str(breath_rate) +
            f"{body_temp:.2f}" +
            str(blood_pressure) +
            str(step_count - prev_step_count) +
            f"{distance - prev_distance:.2f}" +
            f"{calories - prev_calories:.2f}"
        )

        check_sum = calculate_checksum(concatenated_str)

        data_to_upload = {
            "time": current_time,
            "heart_rate": str(heart_rate),
            "breath_rate": str(breath_rate),
            "body_temp": f"{body_temp:.2f}",
            "blood_pressure": str(blood_pressure),
            "step_count": str(step_count - prev_step_count),  # 上传步数增量
            "distance": f"{distance - prev_distance:.2f}",  # 上传距离增量
            "calories": f"{calories - prev_calories:.2f}",  # 上传卡路里增量
            "check_sum": str(check_sum)
        }

        upload_data(machine_id, data_to_upload, check_sum, current_time, message_box, frame)

        data_values = {
            '心率': heart_rate,
            '呼吸率': breath_rate,
            '体温': f"{body_temp:.2f}°C",
            '血压': blood_pressure,
            '步数': step_count,  # 显示累计步数
            '运动距离': f"{distance:.2f} 米",  # 显示累计距离
            '消耗的卡路里': f"{calories:.2f} 卡",  # 显示累计卡路里
            '运动时长': f"{int(elapsed_time.total_seconds() // 60)} 分 {int(elapsed_time.total_seconds() % 60)} 秒"
        }

        for label_text in labels:
            value_label = entries.get(label_text)
            if value_label:
                value_label.config(text=str(data_values[label_text]))

        frame.after(10000, update_data)

    data_frame = tk.Frame(frame)  # 创建一个内部frame用于放置标签和值
    data_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

    for i, label_text in enumerate(labels):
        ttk.Label(data_frame, text=label_text).pack(anchor=tk.W, padx=5, pady=2)
        entries[label_text] = ttk.Label(data_frame, text="0")
        entries[label_text].pack(anchor=tk.W, padx=5, pady=2)

    row = len(container.grid_slaves())  # 计算新行号
    frame.grid(row=row, column=0, padx=5, pady=5, sticky="nsew")
    update_data()

def on_confirm():
    try:
        num_machines = int(entry.get())
        if num_machines <= 0:
            raise ValueError("请输入一个正整数")
        
        global container
        if 'container' in globals() and container is not None:
            container.destroy()
        
        canvas = tk.Canvas(root)
        scrollbar = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
        container = tk.Frame(canvas)

        container.bind(
            "<Configure>",
            lambda e: canvas.configure(
                scrollregion=canvas.bbox("all")
            )
        )

        canvas.create_window((0, 0), window=container, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)

        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        for _ in range(num_machines):
            machine_id = generate_machine_id()
            success, message = register_machine(machine_id)
            if success or (not success and 'has been registered' in message):
                create_frame_for_machine(container, machine_id)
            else:
                messagebox.showerror("Error", f"Failed to register machine ID {machine_id}: {message}")
                
    except ValueError as e:
        messagebox.showerror("Invalid Input", str(e))

# 主应用
root = tk.Tk()
root.title("Machine Registration")

label = tk.Label(root, text="Enter the number of machines:")
label.pack(pady=10)

entry = tk.Entry(root)
entry.pack(pady=10)

button = tk.Button(root, text="Confirm", command=on_confirm)
button.pack(pady=20)

# 初始化全局变量container
container = None

root.mainloop()